
mb.lang = {
  defineClass : function(props, superClass) {
    var f = function() {
      this.initialize.apply(this, arguments);
    }
    f.prototype = {};
    if (superClass && superClass.prototype) {
      jQuery.extend(f.prototype, superClass.prototype)
    }
    jQuery.extend(f.prototype, props);
    return f;
  }
  ,
  keys : function(obj) {
    var keys = [];
    for (var prop in obj) {
      if (!jQuery.isFunction(obj[prop])) keys.push(prop);
    }
    return keys;
  }
  ,
  values : function(obj) {
    var values = [];
    for (var prop in obj) {
      if (!jQuery.isFunction(obj[prop])) values.push(obj[prop]);
    }
    return values;
  }
  ,
  forEach : function(arr, callback, thisObj) {
    for (var i=0,len=arr.length; i<len; i++) {
      callback.call(thisObj, arr[i], i, arr);
    }
  }
  ,
  map : function(arr, callback, thisObj) {
    for (var i=0,ret=[],len=arr.length; i<len; i++) {
      ret.push(callback.call(thisObj, arr[i], i, arr));
    }
    return ret;
  }
  ,
  find : function(arr, callback, thisObj) {
    for (var i=0,len=arr.length; i<len; i++) {
      if (callback.call(thisObj, arr[i], i, arr)) return arr[i];
    }
  }
  ,
  bind : function(func, thisObj) {
    return function() { func.apply(thisObj, arguments) }
  }
  ,

  /**
   * Returns a new function thats evaluate the function passed as param with 
   * a delay.
   * @param func the function
   * @param msec the delay in miliseconds
   */
  delay : function(func, msec) {
    return function() {
      var _this = this;
      var arg = arguments;
      window.setTimeout(function() {
        func.apply(_this, arg); 
      }, msec);
    }
  }
  ,
  poll : function(params) {
    var work = params.work;
    var scope = params.scope;
    var callback = params.callback || mb.lang.emptyFunc; 
    var errback = params.errback || callback;
    var timeout = params.timeout;
    var interval = params.interval || 1000;
    var startTime = new Date().getTime();

    var PID = window.setInterval(function() {
      if (work()) {
        window.clearInterval(PID);
        callback.call(scope);
      } else if (timeout && startTime+timeout < new Date().getTime()) {
        cancel();
      }
    }, interval);

    function cancel() {
      if (PID) {
        window.clearInterval(PID);
        PID = null;
        errback.call(scope);
      }
    }
    return cancel;
  }
  ,
  isString : function(a) {
    return (typeof a == "string" || a instanceof String);
  }
  ,
  isObject : function(a) {
    if (typeof a == 'undefined') return false;
    return (typeof a == 'object' || a === null || jQuery.isArray(a));
  }
  ,
  isArrayLike : function(a) {
    return jQuery.isArray(a) || a && !mb.lang.isString(a) && isFinite(a.length) 
  }
  ,
  isDOMNode : function(a) {
    return a && a.childNodes && jQuery.isArrayLike(a.childNodes)
  }
  ,
  isPrintable : function(a) {
    var t = typeof(a);
    return (t == 'string' || t == 'number' || t == 'boolean' ||
            t == 'undefined' || a === null || 
            a instanceof String || a instanceof Number || a instanceof Boolean ||
            a instanceof Date || mb.lang.isDOMNode(a) );
  }
  ,
  isSerializable : function(a) {
    return mb.lang.isPrintable(a) || mb.lang.isObject(a); 
  }
  ,
  toString : function(a) {
    if (a===null) {
      return 'null';
    } 
	else if (typeof(a)=='undefined') {
      return 'undefined';
    } 
	else if (mb.lang.isDOMNode(a)) {
      if (a.tagName) {
        return '<'+a.tagName.toLowerCase() + ' ' + jQuery.map(a.attributes || [], function(attr) {
          return attr.nodeName + '="' + attr.nodeValue + '"';
        }).join(' ')+'>';
      } 
	  else {
        return a.nodeName;
      }
    } 
	else {
      return (a).toString();
    }
  }
  ,
  toJSON : function(obj) {
    if (mb.lang.isPrintable(obj)) {
      if (obj===null) return 'null';
      if (typeof(obj)=='undefined') return 'undefined';
      if (typeof(obj)=='number') return (obj).toString();
      return '"' + mb.lang.toString(obj)
                      .replace(/(\\|\")/g, '\\$1')
                      .replace(/\n|\r|\t/g, function(a){
                         return (a=='\n') ? '\\n':
                                (a=='\r') ? '\\r':
                                (a=='\t') ? '\\t': ''
                      }) + '"';
    } 
	else if (jQuery.isArray(obj)) {
      return '['+
        jQuery.map(
          jQuery.grep(obj, function(a) {
            return mb.lang.isSerializable(a);
          }),
          function(a) {
            return mb.lang.toJSON(a);
          }
        ).join(',') + ']';
    } 
	else {
      return '{'+
        mb.lang.map(
          jQuery.grep(mb.lang.keys(obj), function(propName) {
            return mb.lang.isSerializable(obj[propName]);
          }),
          function(propName) {
            return '"'+propName+'":'+mb.lang.toJSON(obj[propName]);
          }
        ).join(',')+'}';
    }
  }
  ,
  parseJSON : function(json) {
    return eval('('+json+')');
  }
  ,
  cast : function(type, obj) {
    var isArray = /\[\]$/.test(type);
    type = type.replace(/\[\]$/,'');
    if (isArray && !jQuery.isArray(obj)) {
      obj = obj && obj.split ? obj.split(/\s*,\s*/) : [ obj ];
    }
    var arr = isArray ? obj : [ obj ];
    arr = mb.lang.map(arr, function(o) {
      try {
        otype = typeof(o); 
        ostr = mb.lang.toString(o);
        switch (type.toLowerCase()) {
          case 'integer' : 
            return /^-?\d+$/.test(ostr) ? parseInt(ostr, 10) : NaN;
          case 'float' : 
            return /^-?\d*(\.\d+)?$/.test(ostr) ? parseFloat(ostr) : NaN;
          case 'boolean' :
            return !(!o || ostr.toLowerCase()=="false" || ostr=="0");
          case 'date' : 
            return new Date(o);
          case 'string' : 
            return o == null || otype == 'undefined' ? o : ostr;
          case 'domnode' : 
            return document.createTextNode(ostr);
          case 'object' : 
          default : 
            return o;
        }
      } catch (e) { 
        return; 
      }
    })
    return isArray ? arr : arr[0];
  }
  ,
  loadScript : function(scriptUrl) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.charset = 'utf-8';
    script.src = scriptUrl;
    mb.dom.scriptContainerElem.appendChild(script);
  }
  ,
  emptyFunc : function() {},

  xmlToJson : function(sourceXml) {
	  var json = {};

	  if (sourceXml.nodeType == 1) {

	   if (sourceXml.attributes.length > 0) {
		 json['@attributes'] = {};
		for (var i = 0; i < sourceXml.attributes.length; i++) {
		  json['@attributes'][sourceXml.attributes[i].nodeName] = sourceXml.attributes[i].nodeValue;
		}
	   }
	  } else if (sourceXml.nodeType == 3) {
		json = sourceXml.nodeValue;
	  }

	  if (sourceXml.hasChildNodes()) {
	   for(var j = 0; j < sourceXml.childNodes.length; j++) {
		if (typeof( json[sourceXml.childNodes[j].nodeName]) == 'undefined') {
		  json[sourceXml.childNodes[j].nodeName] = mb.lang.xmlToJson(sourceXml.childNodes[j]);
		} else {
		 if (typeof( json[sourceXml.childNodes[j].nodeName].length) == 'undefined') {
		  var old =  json[sourceXml.childNodes[j].nodeName];
		   json[sourceXml.childNodes[j].nodeName] = [];
		   json[sourceXml.childNodes[j].nodeName].push(old);
		 }
		  json[sourceXml.childNodes[j].nodeName].push(mb.lang.xmlToJson(sourceXml.childNodes[j]));
		}
	   }
	  }
	  return  json;
 }
};